﻿package yii.db.schema{

	import yii.*;
	import phpas3.*;
	import yii.db.*;

	public class CDbSchema{

		private var _builder:CDbCommandBuilder;
		private var _connection:CDbConnection;
		private var _tableNames:Array = new Array();
		private var _tables:Array = new Array();
		private var _cacheExclude:Array = new Array();

		protected function get connection():CDbConnection{
			return _connection;
		}

	 public function get columnTypes():Object{
	 	 return {};
	}







		public function CDbSchema( conn:CDbConnection ){
			this._connection = conn;
		}




	/**
	 * Converts an abstract column type into a physical column type+
	 * The conversion is done using the type map specified in {@link columnTypes}+
	 * These abstract column types are supported (using MySQL as example to explain the corresponding
	 * physical types):
	 * <ul>
	 * <li>pk: an auto-incremental primary key type, will be converted into "int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY"</li>
	 * <li>string: string type, will be converted into "varchar(255)"</li>
	 * <li>text: a long string type, will be converted into "text"</li>
	 * <li>integer: integer type, will be converted into "int(11)"</li>
	 * <li>boolean: boolean type, will be converted into "tinyint(1)"</li>
	 * <li>float: float number type, will be converted into "float"</li>
	 * <li>decimal: decimal number type, will be converted into "decimal"</li>
	 * <li>datetime: datetime type, will be converted into "datetime"</li>
	 * <li>timestamp: timestamp type, will be converted into "timestamp"</li>
	 * <li>time: time type, will be converted into "time"</li>
	 * <li>date: date type, will be converted into "date"</li>
	 * <li>binary: binary data type, will be converted into "blob"</li>
	 * </ul>
	 *
	 * If the abstract type contains two or more parts separated by spaces (e+g+ "string NOT NULL"), then only
	 * the first part will be converted, and the rest of the parts will be appended to the conversion result+
	 * For example, 'string NOT NULL' is converted to 'varchar(255) NOT NULL'+
	 * @param string $type abstract column type
	 * @return string physical column type+
	 * @since 1+1+6
	 */
    public function getColumnType( $type:String ):String{
    	var $pos = strpos($type,' ');
    	if(isset(this.columnTypes[$type])){
    		return this.columnTypes[$type];
    	}else if( $pos!==false ){
    		 var $t = substr( $type, 0, $pos );
    		return (isset(this.columnTypes[$t]) ? this.columnTypes[$t] : $t) + substr($type,$pos);
    	} else {
    		return $type;
    	}
    }



	/**
	 * Builds a SQL statement for creating a new index.
	 * @param string $name the name of the index. The name will be properly quoted by the method.
	 * @param string $table the table that the new index will be created for. The table name will be properly quoted by the method.
	 * @param string $column the column(s) that should be included in the index. If there are multiple columns, please separate them
	 * by commas. Each column name will be properly quoted by the method, unless a parenthesis is found in the name.
	 * @param boolean $unique whether to add UNIQUE constraint on the created index.
	 * @return string the SQL statement for creating a new index.
	 * @since 1.1.6
	 */
	public function createIndex( $name:String, $table:String, $column:String, $unique:Boolean=false ):String{
		var $cols:Array = new Array();
		var $columns = preg_split( /\s*,\s*/, $column, -1, Constants.PREG_SPLIT_NO_EMPTY );
		for( var index in $columns ){
			var $col = $columns[index];
			if( strpos($col,'(') !== false )
				$cols.push( $col );
			else
				$cols.push( this.quoteColumnName($col) );
		}
		return ($unique ? 'CREATE UNIQUE INDEX ' : 'CREATE INDEX ') + this.quoteTableName($name) +' ON '+ this.quoteTableName($table) +' ('+ implode(', ',$cols) +')';
	}


	/**
	 * Builds a SQL statement for creating a new DB table+
	 *
	 * The columns in the new  table should be specified as name-definition pairs (e+g+ 'name'=>'string'),
	 * where name stands for a column name which will be properly quoted by the method, and definition
	 * stands for the column type which can contain an abstract DB type+
	 * The {@link getColumnType} method will be invoked to convert any abstract type into a physical one+
	 *
	 * If a column is specified with definition only (e+g+ 'PRIMARY KEY (name, type)'), it will be directly
	 * inserted into the generated SQL+
	 *
	 * @param string $table the name of the table to be created+ The name will be properly quoted by the method+
	 * @param array $columns the columns (name=>definition) in the new table+
	 * @param string $options additional SQL fragment that will be appended to the generated SQL+
	 * @return string the SQL statement for creating a new DB table+
	 * @since 1+1+6
	 */
	public function createTable( $table:String, $columns:Object, $options:String=null ):String{
		var $cols:Array = new Array();
		for( var $name in $columns ){
			var $type = $columns[$name];
			if( is_string($name) )
				$cols.push( "\t"+ this.quoteColumnName($name) +' '+ this.getColumnType($type) );
			else
				$cols.push( "\t"+ $type );
		}
		var $sql:String = "CREATE TABLE "+ this.quoteTableName($table) +" (\n"+ implode(",\n",$cols) +"\n)";
		return $options===null ? $sql : $sql+' '+$options;
	}




	/**
	 * Quotes a table name for use in a query+
	 * If the table name contains schema prefix, the prefix will also be properly quoted+
	 * @param string $name table name
	 * @return string the properly quoted table name
	 * @see quoteSimpleTableName
	 */
	public function quoteTableName( $name ):String{
		if( strpos($name,'.')===false ){
			return this.quoteSimpleTableName($name);
		}
		var $parts = explode( '.', $name );
		for( var $i in $parts ){
			var $part = $parts[$i];
			$parts[$i] = this.quoteSimpleTableName($part);
		}
		return implode('+',$parts);

	}



	/**
	 * Obtains the metadata for the named table+
	 * @param string $name table name
	 * @return CDbTableSchema table metadata+ Null if the named table does not exist+
	 */
	public function getTable( $name:String ):CDbTableSchema{
		if( isset(this._tables[$name]) ){
			return this._tables[$name];
		} else {
			if( this._connection.tablePrefix && strpos($name,'{{')!==false ){
				var $realName:String = preg_replace( /\{\{(+*?)\}\}/, this._connection.tablePrefix+'$1', $name );
			} else {
				var $realName:String = $name;
			}
			var $table:CDbTableSchema = this.loadTable( $realName );
			this._tables[$name] = $table;
			return $table;
		}
	}




	/**
	 * Quotes a simple column name for use in a query+
	 * A simple column name does not contain prefix+
	 * @param string $name column name
	 * @return string the properly quoted column name
	 * @since 1+1+6
	 */
	public function quoteSimpleColumnName($name:String):String{
		return '"'+$name+'"';
	}



	/**
	 * Quotes a column name for use in a query.
	 * If the column name contains prefix, the prefix will also be properly quoted.
	 * @param string $name column name
	 * @return string the properly quoted column name
	 * @see quoteSimpleColumnName
	 */
	public function quoteColumnName( $name:String ):String{
		var $pos = strrpos($name,'.');
		if( $pos !== false){
			var $prefix=this.quoteTableName(substr($name,0,$pos))+'.';
			$name=substr($name,$pos+1);
		} else {
			var $prefix='';
		}
		return $prefix + ($name==='*' ? $name : this.quoteSimpleColumnName($name));
	}











		protected function loadTable( $name:String ):CDbTableSchema{
			return null;
		}

//        public function getTable($name,$refresh=false):CDbTableSchema{
//                if($refresh===false && isset(this._tables[$name])){
//                        return this._tables[$name];
//                } else {
//                        if(this._connection.tablePrefix!==null && strpos($name,'{{')!==false){
//                                //var $realName:String = preg_replace('/\{\{(.*?)\}\}/',this._connection.tablePrefix.'$1',$name);
//                                var $realName:String = $name.replace(/\{\{(.*?)\}\}/, this._connection.tablePrefix +'$1' );
//                        } else {
//                        		var $realName:String = $name;
//                        }
//                        // temporarily disable query caching
//                        if(this._connection.queryCachingDuration>0){
//                                var $qcDuration = this._connection.queryCachingDuration;
//                                this._connection.queryCachingDuration=0;
//                        }
//                        var $duration:int = this._connection.schemaCachingDuration;
//                        //var $cache = Yii::app().getComponent( this._connection.schemaCacheID );
//                        var $cache = null;
//                        if(!isset(this._cacheExclude[$name]) && $duration>0 && this._connection.schemaCacheID!==false && $cache!==null){
//                                var $key:String = 'yii:dbschema'+ this._connection.connectionString +':'+ this._connection.username +':'+ $name;
//                                var $table = $cache.get($key);
//                                if($refresh===true || $table===false){
//                                        $table=this.loadTable($realName);
//                                        if($table!==null){
//                                                $cache.set($key,$table,$duration);
//                                        }
//                                }
//                                this._tables[$name]=$table;
//                        } else {
//                        		this._tables[$name]=$table=this.loadTable($realName);
//						}
//                        if(isset($qcDuration)){  // re-enable query caching
//                                this._connection.queryCachingDuration=$qcDuration;
//                        }
//                        return $table;
//                }
//        }






        public function quoteSimpleTableName( $name:String ):String{
                return "'"+ $name +"'";
        }








		public function getCommandBuilder():CDbCommandBuilder{
			if( this._builder === null ){
				this._builder = this.createCommandBuilder()
			}
			return this._builder;
		}


		public function getDbConnection():CDbConnection{
			return this._connection;
		}

		protected function createCommandBuilder():CDbCommandBuilder{
			return new CDbCommandBuilder( this );
		}


	}
}